<?php


namespace App\Middleware\Api;


use App\Utils\SignUtils;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
use Psr\Http\Server\RequestHandlerInterface;

/**
 * 服务器接入验签
 *
 * @package App\Middleware\Api
 */
class PayServerSignMiddleware implements MiddlewareInterface {

    /**
     * @Inject()
     * @var ValidatorFactoryInterface
     */
    protected $validationFactory;

    /**
     * @param ServerRequestInterface  $request
     * @param RequestHandlerInterface $handler
     *
     * @return ResponseInterface
     * @throws \Exception
     */
    public function process (ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {

        $parseBody = $request->getParsedBody();

        $this->_validOrFail($parseBody, [
            'sign' => 'required',
        ]);

        $ext = $request->getAttribute('ext', []);

        $mchInfo = Arr::get($ext, 'mchInfo');

        $this->_verifySign($mchInfo, $parseBody);

        return $handler->handle($request);
    }

    /**
     * 验签
     *
     * @param $mch
     * @param $data
     *
     * @throws \Exception
     */
    protected function _verifySign ($mch, $data) {

        if (config('isDev')) {
            // 开发模式无需验签
            return;
        }

        if (!$mch['needSign']) {
            // 无需验签
            return;
        }

        if (!SignUtils::verifySign($data, $mch['appSecret'])) {
            throw new \Exception(trans('pay.sign.verify.fail'));
        }
    }

    /**
     * 表单校验抛异常封装
     *
     * @param       $params
     * @param array $rules
     * @param array $messages
     * @param array $customAttrs
     *
     * @throws \Exception
     */
    protected function _validOrFail ($params, $rules = [], $messages = [], $customAttrs = []) {

        $validator = $this->validationFactory->make($params, $rules, $messages, $customAttrs);

        if ($validator->fails()) {
            $errorMessage = $validator->errors()->first();
            throw new \Exception($errorMessage);
        }
    }

}
